package com.arpa.oms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.oms.domain.enums.SaleTypeEnum;
import com.arpa.oms.service.ISaleOrderService;
import com.arpa.wms.domain.consts.CommonConst;
import com.arpa.wms.domain.consts.NumConst;
import com.arpa.wms.domain.dto.DeliveryDistributionDTO;
import com.arpa.wms.domain.dto.DeliveryItemDTO;
import com.arpa.wms.domain.dto.InventoryDTO;
import com.arpa.wms.domain.entity.*;
import com.arpa.wms.domain.enums.DeliverySourceTypeEnum;
import com.arpa.wms.domain.enums.DeliveryStatusEnum;
import com.arpa.wms.domain.enums.InventoryRecordTypeEnum;
import com.arpa.wms.domain.vo.UsefulInventoryVO;
import com.arpa.wms.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import static com.arpa.wms.domain.consts.NumConst.NUM_ZERO;

@Slf4j
@Service
public class SaleOrderServiceImpl implements ISaleOrderService {

    @Autowired
    private IDeliveryService iDeliveryService;
    @Autowired
    private IDeliveryItemService iDeliveryItemService;
    @Autowired
    private ILocationService locationService;
    @Autowired
    private IInventoryService iInventoryService;
    @Autowired
    private IDeliveryDistributionService iDeliveryDistributionService;
    @Autowired
    private IInventoryRecordService iInventoryRecordService;
    /**
     * 自动分配库存
     * @param deliveryCode
     * @return
     */
    @Override
    @Transactional(rollbackFor = {ServiceException.class})
    public Result autoDistribute(String deliveryCode) {
        Delivery delivery = iDeliveryService.getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, deliveryCode));
        Integer isDeliveringIfStockout = delivery.getIsDeliveringIfStockout();
        InventoryDTO inventoryDTO;
        // 发货单新锁定数量
        BigDecimal totalNewLockQuantity = BigDecimal.ZERO;
        // 查询获取明细
        List<DeliveryItem> deliveryItems = iDeliveryItemService.list(new QueryWrapper<DeliveryItem>().lambda()
                .eq(DeliveryItem::getDeliveryCode, deliveryCode).eq(DeliveryItem::getDeleted, 0));
        //判断订单明细的商品编码和数量是否和预打包库位上的fastMatching相匹配
        List<Inventory> prepackagedInvList = matchPrepackagedInv(deliveryItems, delivery);
        //判断是否完全是自动分配
        boolean autoDistribute = delivery.getDistributionNum().compareTo(BigDecimal.ZERO) == 0;
        //完全自动分配和订单明细商品编码匹配预打包fastMatching字段时，先用预打包分配库存
        if (autoDistribute && IterUtil.isNotEmpty(prepackagedInvList)) {
            //订单分配预打包库存
            totalNewLockQuantity = prepackagedInvDistribute(prepackagedInvList,deliveryItems,totalNewLockQuantity,delivery);
            //根据库存获取预打包单号
         /*   PrepackagedDistribution prepackagedDistribution = prepackagedDistributionService.list(new LambdaQueryWrapper<PrepackagedDistribution>()
                    .select(PrepackagedDistribution::getPrepackagedCode)
                    .eq(PrepackagedDistribution::getInventoryCode, prepackagedInvList.get(NUM_ZERO).getOldInventoryCode())).get(NUM_ZERO);*/
            // 更新发货单分配数量和预打包标识
            iDeliveryService.update(new UpdateWrapper<Delivery>().lambda()
                    .eq(Delivery::getCode, deliveryCode)
                    .set(Delivery::getPrepackaged, NumConst.NUM_ONE)
                    .set(Delivery::getPackIssue,NumConst.NUM_ONE)
                    .setSql(" distribution_num = distribution_num + " + totalNewLockQuantity)
                    .set(Delivery::getPrepackagedCode,prepackagedInvList.get(NUM_ZERO).getTempCode()));
        } else {
            // 过滤掉已完全分配的明细，即保留明细总数-分配数量 >0的数据
            deliveryItems = deliveryItems.stream().filter(e->
                    e.getDistributionQuantity()!=null && e.getTotalNum().subtract(e.getDistributionQuantity()).compareTo(BigDecimal.ZERO) > 0
            ).collect(Collectors.toList());
            // 遍历这些待分配明细，获取库存，分配库存
            for(DeliveryItem deliveryItem: deliveryItems){
                inventoryDTO = new InventoryDTO();
                inventoryDTO.setWarehouseCode(delivery.getWarehouseCode());
                inventoryDTO.setShipmentCode(delivery.getShipmentCode());
                inventoryDTO.setGoodsCode(deliveryItem.getGoodsCode());

                // 剩余待分配数量
                BigDecimal remainQuantity = deliveryItem.getTotalNum().subtract(deliveryItem.getDistributionQuantity());
                // 库存可用数量
                BigDecimal usefulQuantity;
                // 待锁定数量
                BigDecimal lockQuantity;
                // 明细新增待锁定数量
                BigDecimal totalItemNewLockQuantity = BigDecimal.ZERO;

                List<UsefulInventoryVO> inventories = iInventoryService.getCanAutoDistributedInventory(inventoryDTO,isDeliveringIfStockout);

                DeliveryDistribution distribution;
                Inventory distributeInventory = new Inventory();
                Inventory tempInventory;
                // 遍历获取明细，根据明细的总数-分配数量>0的即需要分配的
                if (IterUtil.isNotEmpty(inventories)) {
                    for(UsefulInventoryVO inventory: inventories){
                        usefulQuantity = inventory.getQuantity().subtract(inventory.getUseQuantity());
                        lockQuantity = usefulQuantity.compareTo(remainQuantity)>0?remainQuantity:usefulQuantity;
                        // 更新主子表新锁定数量
                        totalItemNewLockQuantity = totalItemNewLockQuantity.add(lockQuantity);
                        totalNewLockQuantity = totalNewLockQuantity.add(lockQuantity);

                        // 插入或更新分配表
                        distribution = insertOrUpdateDistribution(inventory, deliveryItem, lockQuantity);

                        // 减去库存
                        reduceInventory(distribution, deliveryItem);

                        BeanUtil.copyProperties(inventory, distributeInventory);
                        // 增加临时库存
                        tempInventory = saveDeliveryTempInventory(distribution, delivery, deliveryItem, distributeInventory);

                        // 临时库位中的库存作业状态改为工作中，避免其他操作使用
                        iInventoryService.setTempInvJobStatusWorking(tempInventory.getCode());

                        // 记录库存变动日志， 原库存减少，临时库存增加
                        iInventoryRecordService.insertRecord(distributeInventory, delivery.getCode(), distribution.getDistributionQuantity().negate(), InventoryRecordTypeEnum.PICK.getValue());
                        iInventoryRecordService.insertRecord(tempInventory, delivery.getCode(), distribution.getDistributionQuantity(), InventoryRecordTypeEnum.PICK.getValue());

                        // 需要回填发货分配的库存，将库存编码指向临时库存
                        iDeliveryDistributionService.update(new UpdateWrapper<DeliveryDistribution>().lambda()
                                .eq(DeliveryDistribution::getCode, distribution.getCode())
                                .set(DeliveryDistribution::getInventoryCode, tempInventory.getCode())
                                .set(DeliveryDistribution::getOldInventoryCode,inventory.getCode())
                        );

                        // 更新剩余数量
                        remainQuantity = remainQuantity.subtract(lockQuantity);

                        // 剩余分配数量为0，则说明完成分配，提前退出循环
                        if(remainQuantity.compareTo(BigDecimal.ZERO) == 0){
                            break;
                        }
                    }
                }

                // 更新明细的分配数量
                if(totalItemNewLockQuantity.compareTo(BigDecimal.ZERO) > 0) {
                    DeliveryItemDTO deliveryItemDTO = new DeliveryItemDTO();
                    deliveryItemDTO.setCode(deliveryItem.getCode());
                    deliveryItemDTO.setDistributionQuantity(totalItemNewLockQuantity);
                    iDeliveryItemService.adjustDistributionQuantity(deliveryItemDTO);
                }
            }
            // 更新发货单分配数量
            iDeliveryService.update(new UpdateWrapper<Delivery>().lambda()
                    .eq(Delivery::getCode, deliveryCode)
                    .setSql(" distribution_num = distribution_num + " + totalNewLockQuantity));
        }

        delivery = iDeliveryService.getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, delivery.getCode()));

        // 如果发货单完全分配，则触发完全确认操作
        if (delivery.getTotalNum().compareTo(delivery.getDistributionNum()) == 0) {
            // 调用确认功能
            iDeliveryService.confirm(deliveryCode, UserUtil.getBranchCode());
        } else if (DeliverySourceTypeEnum.OMS.getValue().equals(delivery.getSourceType()) && !SaleTypeEnum.NORMAL.getValue().equals(delivery.getOrderType())){
            // 如果数量不足，但有分配，则状态调整为部分分配
            if (delivery.getDistributionNum().compareTo(BigDecimal.ZERO) > 0) {
                iDeliveryService.update(new UpdateWrapper<Delivery>().lambda().eq(Delivery::getCode, delivery.getCode())
                        .set(Delivery::getStatus, DeliveryStatusEnum.PART_DISTRIBUTION.getValue()));
            }
        }

        return Result.ok();
    }

    private List<Inventory> matchPrepackagedInv(List<DeliveryItem> deliveryItems,Delivery delivery) {
        List<DeliveryItem> sortedItem = deliveryItems.stream().sorted(Comparator.comparing(DeliveryItem::getGoodCode))
                .collect(Collectors.toList());
        DeliveryItem firstItem = sortedItem.get(NUM_ZERO);
        //生成快速匹配号
        StringBuilder sb = new StringBuilder();
        sortedItem.stream().forEach(e -> {
            sb.append(e.getGoodCode() + "_" + e.getTotalNum() + "#");
        });
        sb.replace(sb.length() - 1, sb.length(), "");
        String fastMatching = sb.toString();
        //获取预打包临时库位
        Location location = locationService.getOne(new LambdaQueryWrapper<Location>()
                .eq(Location::getWarehouseCode, delivery.getWarehouseCode())
                .eq(Location::getSerialNumber, CommonConst.YDKW));
        List<Inventory> invList =  iInventoryService.list(new LambdaQueryWrapper<Inventory>()
                .eq(Inventory::getWarehouseCode, delivery.getWarehouseCode())
                .eq(Inventory::getShipmentCode, delivery.getShipmentCode())
                .eq(Inventory::getLocation, location.getCode())
                .eq(Inventory::getFastMatching, fastMatching)
                .apply("quantity > use_quantity"));
        //判断预打包库存是否充足
        List<Inventory> filterInv = invList.stream().filter(e -> e.getGoodCode().equals(firstItem.getGoodCode())
                && (e.getQuantity().subtract(e.getUseQuantity()).compareTo(firstItem.getTotalNum()) >= 0))
                .collect(Collectors.toList());
        if (IterUtil.isEmpty(filterInv)) {
            return filterInv;
        } else {
            return invList;
        }
    }

    private BigDecimal prepackagedInvDistribute(List<Inventory> prepackagedInvList,List<DeliveryItem> deliveryItems,
                                                BigDecimal totalNewLockQuantity,Delivery delivery) {
        for(DeliveryItem deliveryItem: deliveryItems){

            // 剩余待分配数量就是明细的总数量
            BigDecimal remainQuantity = deliveryItem.getTotalNum();
            // 库存可用数量
            BigDecimal usefulQuantity;
            // 待锁定数量
            BigDecimal lockQuantity;
            // 明细新增待锁定数量
            BigDecimal totalItemNewLockQuantity = BigDecimal.ZERO;
            //过滤掉其他商品
            List<Inventory> filterPrepackagedInvList = prepackagedInvList.stream().filter(info -> info.getGoodCode().equals(deliveryItem.getGoodCode()))
                    .collect(Collectors.toList());
            List<UsefulInventoryVO> inventories = filterPrepackagedInvList.stream().map(info -> {
                UsefulInventoryVO inventoryVO = new UsefulInventoryVO();
                BeanUtil.copyProperties(info, inventoryVO);
                return inventoryVO;
            }).collect(Collectors.toList());
            DeliveryDistribution distribution;
            Inventory distributeInventory = new Inventory();
            Inventory tempInventory;
            // 遍历获取明细，根据明细的总数-分配数量>0的即需要分配的
            if (IterUtil.isNotEmpty(inventories)) {
                for(UsefulInventoryVO inventory: inventories){
                    usefulQuantity = inventory.getQuantity().subtract(inventory.getUseQuantity());
                    lockQuantity = usefulQuantity.compareTo(remainQuantity)>0?remainQuantity:usefulQuantity;
                    // 更新主子表新锁定数量
                    totalItemNewLockQuantity = totalItemNewLockQuantity.add(lockQuantity);
                    totalNewLockQuantity = totalNewLockQuantity.add(lockQuantity);

                    // 插入或更新分配表
                    distribution = insertOrUpdateDistribution(inventory, deliveryItem, lockQuantity);

                    // 减去库存
                    reduceInventory(distribution, deliveryItem);

                    BeanUtil.copyProperties(inventory, distributeInventory);
                    // 增加临时库存
                    tempInventory = saveDeliveryTempInventory(distribution, delivery, deliveryItem, distributeInventory);

                    // 临时库位中的库存作业状态改为工作中，避免其他操作使用
                    iInventoryService.setTempInvJobStatusWorking(tempInventory.getCode());

                    // 记录库存变动日志， 原库存减少，临时库存增加
                    iInventoryRecordService.insertRecord(distributeInventory, delivery.getCode(), distribution.getDistributionQuantity().negate(), InventoryRecordTypeEnum.PICK.getValue());
                    iInventoryRecordService.insertRecord(tempInventory, delivery.getCode(), distribution.getDistributionQuantity(), InventoryRecordTypeEnum.PICK.getValue());

                    // 需要回填发货分配的库存，将库存编码指向临时库存
                    iDeliveryDistributionService.update(new UpdateWrapper<DeliveryDistribution>().lambda()
                            .eq(DeliveryDistribution::getCode, distribution.getCode())
                            .set(DeliveryDistribution::getInventoryCode, tempInventory.getCode())
                            .set(DeliveryDistribution::getOldInventoryCode,inventory.getCode())
                    );

                    // 更新剩余数量
                    remainQuantity = remainQuantity.subtract(lockQuantity);

                    // 剩余分配数量为0，则说明完成分配，提前退出循环
                    if(remainQuantity.compareTo(BigDecimal.ZERO) == 0){
                        break;
                    }
                }
            }

            // 更新明细的分配数量
            if(totalItemNewLockQuantity.compareTo(BigDecimal.ZERO) > 0) {
                DeliveryItemDTO deliveryItemDTO = new DeliveryItemDTO();
                deliveryItemDTO.setCode(deliveryItem.getCode());
                deliveryItemDTO.setDistributionQuantity(totalItemNewLockQuantity);
                iDeliveryItemService.adjustDistributionQuantity(deliveryItemDTO);
            }
        }
        return totalNewLockQuantity;
    }

    /**
     * 插入或更新分配表，根据库存、分配明细、待锁定数量
     * @param inventory
     * @param deliveryItem
     * @param lockQuantity
     * @return
     */
    private DeliveryDistribution insertOrUpdateDistribution(UsefulInventoryVO inventory, DeliveryItem deliveryItem, BigDecimal lockQuantity){
        // 查询是否有相同分配数据且未生成拣货单，有则在起基础上增加，没有则插入一条新的
        DeliveryDistribution sameDistribution = iDeliveryDistributionService.getOne(new QueryWrapper<DeliveryDistribution>().lambda()
                .eq(DeliveryDistribution::getDeliveryItemCode, deliveryItem.getCode())
                .eq(DeliveryDistribution::getOldInventoryCode, inventory.getCode())
                .eq(DeliveryDistribution::getPickingCode, StrUtil.EMPTY)
                .eq(DeliveryDistribution::getDeleted, 0));
        if(sameDistribution == null){
            // 插入新的分配数据
            sameDistribution = new DeliveryDistribution();
            BeanUtil.copyProperties(inventory, sameDistribution);
            sameDistribution.setCode(IdUtil.simpleUUID());
            sameDistribution.setInventoryCode(inventory.getCode());
            sameDistribution.setGoodsUnit(deliveryItem.getGoodsUnit());
            sameDistribution.setGoodsUnitName(deliveryItem.getGoodsUnitName());
            sameDistribution.setDeliveryCode(deliveryItem.getDeliveryCode());
            sameDistribution.setDeliveryItemCode(deliveryItem.getCode());
            sameDistribution.setRemarks(deliveryItem.getRemarks());
            sameDistribution.setPickingCode(StrUtil.EMPTY);
            sameDistribution.setDistributionQuantity(lockQuantity);
            sameDistribution.setPlanQuantity(lockQuantity);
            sameDistribution.setOldInventoryCode(inventory.getCode());
            iDeliveryDistributionService.save(sameDistribution);
        }else{
            DeliveryDistributionDTO distributionDTO = new DeliveryDistributionDTO();
            distributionDTO.setCode(sameDistribution.getCode());
            distributionDTO.setDistributionQuantity(lockQuantity);
            distributionDTO.setPlanQuantity(lockQuantity);
            iDeliveryDistributionService.adjustDistributionQuantity(distributionDTO);
        }

        return sameDistribution;
    }

    /**
     * 减少库存
     * @param distribution
     * @param deliveryItem
     * @return
     */
    private Result reduceInventory(DeliveryDistribution distribution, DeliveryItem deliveryItem){
        // 库存变动量 = 分配数量
        BigDecimal changeQuantity = distribution.getDistributionQuantity();

        // 原库存上扣除已分配的库存,
        boolean flag = iInventoryService.reduceQuantity(distribution.getInventoryCode(), changeQuantity);
        if (!flag) {
            throw new ServiceException("分配数量超过了可用数量，无法分配");
        }
        return Result.ok();
    }

    /**
     * 分配时，保存分配临时库存信息
     * @param distribution
     * @param delivery
     * @param deliveryItem
     * @return
     */
    private Inventory saveDeliveryTempInventory(DeliveryDistribution distribution, Delivery delivery, DeliveryItem deliveryItem, Inventory inventory){
        // 库存变动量 = 分配数量
        BigDecimal changeQuantity = distribution.getDistributionQuantity();
        // 判断是否有相同的临时库存，如果有，则在其基础上增加数量，如果没有，则新增一条
        Inventory sameInventory = iInventoryService.hasSameDeliveryTempInventory(distribution, delivery);
        if(sameInventory == null){
            sameInventory = iInventoryService.saveDeliveryTempInventory(distribution, delivery, deliveryItem, inventory);
        }else{
            // 增加临时库存
            iInventoryService.adjustQuantity(sameInventory.getCode(), changeQuantity);
        }

        return sameInventory;
    }
}
